#pragma once
#include "online.hpp"
#include <unordered_map>
#define SESSION_FOREVER -1

typedef enum{UNLOGIN, LOGIN} ss_state;
class session
{
public:
    session(int session_id):_session_id(session_id) { debug("%s -> %d", "session已经被创建", _session_id); }
    ~session(){ debug("%s -> %d", "session已经被销毁", _session_id); }
    int session_id() { return _session_id; }
    void set_user(int uid) { _user_id = uid; }
    int get_user() { return _user_id; }
    void set_state(ss_state state) { _state = state; }
    bool is_login(){ return (_state == LOGIN); }
    void set_timer(const ws_server_t::timer_ptr &tp) { _tp = tp; }
    ws_server_t::timer_ptr &get_timer() { return _tp; }
private:
    int _session_id;
    int _user_id;
    ss_state _state;
    ws_server_t::timer_ptr _tp;
};

class session_manager
{
public:
    session_manager(ws_server_t *server):_server(server) { debug("%s", "session_manager create!!"); }
    ~session_manager() { debug("%s", "session_manager destroy!!"); }
    std::shared_ptr<session> create_session(int uid, ss_state state) 
    {
        std::unique_lock<std::mutex> lock(_mutex);
        std::shared_ptr<session> s = std::make_shared<session>(_session_count);
        s->set_state(state);
        s->set_user(uid);
        _sessions.emplace(_session_count, s);
        _session_count++;
        return s;
    }
    void append(const std::shared_ptr<session> &ssp) 
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.emplace(ssp->session_id(), ssp);
    }
    std::shared_ptr<session> get_session(int session_id) 
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto iter = _sessions.find(session_id);
        if(iter == _sessions.end()) { return std::shared_ptr<session>(); }
        return iter->second;
    }
    void remove_session(int session_id) 
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_sessions.count(session_id) == 0) return ;
        _sessions.erase(session_id);
    }
    void set_expired_time(int session_id, int ms) 
    {
        std::shared_ptr<session> ssp = get_session(session_id); // 获取session_id对应的session
        if(ssp.get() == nullptr) { return;} /*对应session不存在，直接返回*/

        ws_server_t::timer_ptr tmp_t = ssp->get_timer();
        if(tmp_t.get() == nullptr && ms == SESSION_FOREVER) {// 没有定时任务，并且我们要设置为永久存在
            return;
        }
        else if(tmp_t.get() == nullptr && ms != SESSION_FOREVER) {// 没有定时任务，但是我们要设置定时任务
            ws_server_t::timer_ptr tm = _server->set_timer(ms, 
            std::bind(&session_manager::remove_session, this, session_id));
            ssp->set_timer(tm);
        }
        else if(tmp_t.get() != nullptr && ms == SESSION_FOREVER) {// 存在定时任务，但是我们要将其设置为永久存在
            // 取消定时任务
            tmp_t->cancel(); // 取消定时任务会导致该任务被立刻执行，即该session会删除
            ssp->set_timer(ws_server_t::timer_ptr()); // 给一个空的定时任务，表示永久
            // 但是取消定时任务这步操作并不会立即被执行，为了避免直接重新添加后，但cancel还没有被执行导致添加失败
            // 所以这里为了避免重新添加失败，在设置一个定时任务，用来重新添加session
            _server->set_timer(0, std::bind(&session_manager::append, this, ssp));
        }
        else if(tmp_t.get() != nullptr && ms != SESSION_FOREVER) {// 存在定时任务，但是我们要重新设置
            // 1.取消之前的定时任务
            tmp_t->cancel();
            ssp->set_timer(ws_server_t::timer_ptr());
            _server->set_timer(0, std::bind(&session_manager::append, this, ssp));
            // 2.重新设置定时任务 
            ws_server_t::timer_ptr tm = _server->set_timer(ms, 
            std::bind(&session_manager::remove_session, this, ssp->session_id()));
            ssp->set_timer(tm);
        }
    }
private:
    int _session_count;
    std::mutex _mutex;
    std::unordered_map<int, std::shared_ptr<session>> _sessions;
    ws_server_t *_server;
};
